using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Web.UI;
using Quiki.ElementFormatters;
using Quiki.ExpressionParsers;
using Quiki.Templates;

namespace Quiki
{
    public class HtmlFormatterContext
    {
        public HtmlFormatterContext(
            XhtmlTextWriter writer, 
            TemplatesRegistry templatesRegistry)
        {
            this.writer = writer;
            this.templatesRegistry = templatesRegistry;

            elementFormatters.Add (new RootElementFormatter());
            elementFormatters.Add (new HeaderElementFormatter ());
            elementFormatters.Add (new ListItemElementFormatter ());
            elementFormatters.Add (
                new LambdaElementFormatter(
                    WikiTextElementType.Paragraph,
                    1,
                    (e, c) =>
                    {
                        if (c.CurrentParagraphType != WikiTextElementType.Paragraph)
                        {
                            c.CloseAllParagraphs();
                            c.CurrentParagraphType = WikiTextElementType.Paragraph;
                            c.PushTag(HtmlTextWriterTag.P);
                        }
                        else
                            c.Writer.WriteLine();
                    },
                    null));
            elementFormatters.Add(new PreElementFormatter());
            elementFormatters.Add(new SimpleElementFormatter(WikiTextElementType.ListUnordered, 1, HtmlTextWriterTag.Li));
            elementFormatters.Add(new SimpleElementFormatter(WikiTextElementType.Bold, HtmlTextWriterTag.Strong));
            elementFormatters.Add(new SimpleElementFormatter(WikiTextElementType.Italic, HtmlTextWriterTag.I));
            elementFormatters.Add(new LambdaElementFormatter(WikiTextElementType.Text, null, null));
            elementFormatters.Add (new ExternalLinkElementFormatter());
            elementFormatters.Add (new ImageElementFormatter(new ImageDataParser()));
            elementFormatters.Add (new InternalLinkElementFormatter());
            elementFormatters.Add (new TemplateFormatter(templatesRegistry));
            elementFormatters.Add (new LambdaElementFormatter (
                WikiTextElementType.NewLine,
                (e, c) => c.CloseAllParagraphs (),
                null));
            elementFormatters.Add(new LambdaElementFormatter(
                                      WikiTextElementType.Virtual,
                                      null,
                                      null));
            elementFormatters.Add(new LambdaElementFormatter(
                WikiTextElementType.Error,
                (e, c) =>
                {
                    c.Writer.AddAttribute (HtmlTextWriterAttribute.Style, "color: red");
                    c.Writer.RenderBeginTag (HtmlTextWriterTag.P);
                    c.Writer.Write (e.Text);
                },
                (e, c) => c.Writer.RenderEndTag ()));
        }

        public WikiTextElementType CurrentParagraphType
        {
            get { return currentParagraphType; }
            set { currentParagraphType = value; }
        }

        public DocumentGraph DocumentGraph
        {
            get { return documentGraph; }
            set { documentGraph = value; }
        }

        public WikiDocument Document
        {
            get { return document; }
            set { document = value; }
        }

        public XhtmlTextWriter Writer
        {
            get { return writer; }
        }

        public void CloseAllParagraphs ()
        {
            while (stackedTags.Count > 0)
                PopTag();

            currentParagraphType = WikiTextElementType.None;
        }

        public void ProcessElement (WikiTextElement element)
        {
            foreach (IElementFormatter formatter in elementFormatters)
            {
                if (formatter.FormatBegin (element, this))
                {
                    if (element.Text != null)
                    {
                        int indent = writer.Indent;

                        if (suppressTabs)
                            writer.Indent = 0;
                        
                        formatter.FormatText(element, this);

                        if (suppressTabs)
                            writer.Indent = indent;
                    }
                    else if (false == element.Terminal)
                    {
                        foreach (WikiTextElement childElement in element.ChildElements)
                            ProcessElement (childElement);
                    }

                    formatter.FormatEnd(element, this);
                    break;
                }
            }
        }

        public void PushTag (HtmlTextWriterTag tag)
        {
            writer.RenderBeginTag(tag);
            stackedTags.Push(tag);
        }

        public void PopTag()
        {
            writer.RenderEndTag();
            stackedTags.Pop();
        }

        public void ResumeTabs ()
        {
            suppressTabs = false;
        }

        public void SuppressTabs ()
        {
            suppressTabs = true;
        }

        private DocumentGraph documentGraph;
        private WikiDocument document;
        [SuppressMessage ("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
        private readonly TemplatesRegistry templatesRegistry;
        private readonly List<IElementFormatter> elementFormatters = new List<IElementFormatter> ();
        private WikiTextElementType currentParagraphType;
        private readonly XhtmlTextWriter writer;
        private Stack<HtmlTextWriterTag> stackedTags = new Stack<HtmlTextWriterTag>();
        private bool suppressTabs;
    }
}